package org.lgiavedoni.android.franq.core.service;

import java.util.Collection;
import java.util.Date;
import java.util.Observable;

import org.lgiavedoni.android.franq.service.config.ConfigService;
import org.lgiavedoni.android.franq.service.log.Logger;
import org.lgiavedoni.android.franq.util.date.DateUtil;
import org.lgiavedoni.android.franq.util.implementation.ImplementationUtil;

public abstract class Service extends Observable implements Runnable, Comparable<Service> {

	private Status mystatus = Status.STOPPED;
	private Date lastModificationDate = null;
	private Boolean forceWait = Boolean.FALSE;
	private Integer priority = new Integer(100);

	protected enum Status {
		DISABLE, STARTED, STOPPED, ERROR;

		@Override
		public String toString() {
			return name();
		}

	}

	public Service() {
	}

	private String getEnableConfigName() {
		return "SERVICE_" + getServiceName().toUpperCase() + "_ENABLE";
	}

	public abstract String getServiceName();

	private synchronized final void start(boolean forceWait) {
		if (isStarted())
			return;
		Logger.inst.info("Starting service[" + getServiceName() + "]...");
		setStatus(Service.Status.STARTED);
		lastModificationDate = new Date();
		// start (before run)
		try {
			startInternal();
			if (forceWait) {
				run();
			} else {
				Thread thread = new Thread(this);
				thread.setName(this.toString());
				thread.start();
			}
			Logger.inst.info("Starting service[" + getServiceName() + "]... END");
		} catch (Exception ex) {
			Logger.inst.error("Startting service[" + getServiceName() + "]... FAILD", ex);
			setStatus(Service.Status.ERROR);
		}
	}

	public synchronized final void start() {
		if (!(this instanceof ConfigService) && ConfigService.inst.isStarted()) {
			if (!ConfigService.inst.getValueAsBoolean(getEnableConfigName())) {
				setStatus(Status.DISABLE);
			}
		}
		if (isEnable())
			start(forceWait);
		else
			Logger.inst.warning("The service [" + getServiceName() + "] can't be started because has been disabled");
	}

	@Override
	public final void run() {
		try {
			_run();
		} catch (Exception ex) {
			Logger.inst.error(ex);
		}

	}

	protected abstract void _run();

	public void restart() {
		Logger.inst.debug("Reloading service[" + getServiceName() + "]...");
		stop();
		start();
	}

	public void stop() {
		Logger.inst.debug("Stopping service[" + getServiceName() + "]...");
		lastModificationDate = new Date();
		setStatus(Service.Status.STOPPED);
		try {
			stopInternal();
			Logger.inst.info("Stopping service[" + getServiceName() + "]... END");
		} catch (Exception e) {
			Logger.inst.error("ERROR Stopping service[" + getServiceName() + "]", e);
		}

	}

	protected abstract void stopInternal() throws Exception;

	protected abstract void startInternal() throws Exception;

	public Status getStatus() {
		return mystatus;
	}

	public boolean isStarted() {
		return mystatus.equals(Status.STARTED) ? true : false;
	}

	public boolean isStopped() {
		return mystatus.equals(Status.STOPPED) ? true : false;
	}

	protected void setStatus(Status status) {
		this.mystatus = status;
	}

	protected Collection<Object> getImplementations(String key) {
		return ImplementationUtil.initialize(ImplementationUtil.getImplementations(key));
	}

	protected Object getImplementation(String key) {
		Collection<Object> c = ImplementationUtil.initialize(ImplementationUtil.getImplementations(key));
		if (c.size() > 1) {
			Logger.inst.warning("More than one implementation for [" + key + "]");
		}
		return c.isEmpty() ? null : c.toArray()[0];
	}

	protected Object getImplementation(Class clazz) {
		Collection<Object> c = ImplementationUtil.initialize(ImplementationUtil.getImplementations(clazz));
		if (c.size() > 1) {
			Logger.inst.warning("More than one implementation for [" + clazz + "]");
		}
		return c.isEmpty() ? null : c.toArray()[0];
	}

	protected Collection<Object> getImplementations(Class clazz) {
		Collection<Object> c = ImplementationUtil.initialize(ImplementationUtil.getImplementations(clazz));
		return c;
	}

	public String getInfo() {
		return getServiceName() + " [" + getStatus() + "]";
	}

	public String getFullInfo() {
		return "Service = " + getServiceName() + ", Status = [" + getStatus() + " on " + DateUtil.parseDateTime(lastModificationDate) + "]";
	}

	@Override
	public String toString() {
		return getServiceName() + " [" + getStatus() + "]";
	}

	public void setForceWait(Boolean forceWait) {
		this.forceWait = forceWait;
	}

	public void setPriority(Integer priority) {
		this.priority = priority;
	}

	public int compareTo(Service service) {
		return priority - service.priority;
	}

	public boolean isEnable() {
		return !mystatus.equals(Status.DISABLE);
	}

	public void setEnable(boolean enable) {
		ConfigService.inst.setValue(getEnableConfigName(), enable);
		ConfigService.inst.updateConfig();
		if (!enable) {
			stop();
			mystatus = Status.DISABLE;
		} else {
			mystatus = Status.STOPPED;
			start();
		}
	}

}
